<?php

/**
 * $Id: ActiveRecordTest.php 184 2008-04-07 09:18:44Z vbolshov $
 */

/**
 * prerequisites
 */

require_once dirname(__FILE__) . '/../../lib/km/db/ActiveRecord.php';
require_once dirname(__FILE__) . '/../../lib/km/db/ActiveRecord/NeighborRelation.php';
require_once dirname(__FILE__) . '/../../lib/km/db/Select.php';
require_once dirname(__FILE__) . '/../../lib/km/util/Validator.php';
require_once dirname(__FILE__) . '/../../lib/km/util/Validator/Result.php';

use km\db\ActiveRecord;

class Test extends ActiveRecord {
	static protected $table = 'activerecordtest';
	static protected $defaults = array(
		'name' => null
	);
	static protected $children = array(
		'child' => array('TestChild', 'parent_id')
	);
	static protected $neighbors = array(
		'n' => array('TestNeighbor', 'test_has_neighbors', 'test_id', 'neighbor_id')
	);
	/**
	 * @return Select
	 */
	function getChildObjects()
	{
		return parent::getChildren('child');
	}
	/**
	 * @return Select
	 */
	function getNeighborObjects()
	{
		return parent::getNeighbors('n');
	}
	function bindNeighbors($ids)
	{
		return parent::setNeighbors('n', $ids);
	}
	function unbindNeighbor($id)
	{
		return parent::removeNeighbor('n', $id);
	}
	
	protected function createValidator()
	{
		return parent::createValidator()
			->addRequired('name', 'name is required')
			->addStringRule('name', 'name should be a string');
	}
}
class TestChild extends ActiveRecord {
	static protected $table = 'activerecordtest_child';
	static protected $defaults = array(
		'parent_id' => null,
		'name' => null,
	);
	static protected $parents = array(
		'parent' => array('Test', 'parent_id')
	);
	/**
	 * @return Select
	 */
	function getParentObject()
	{
		return parent::getParent('parent');
	}
}

class TestNeighbor extends ActiveRecord {
	static protected $table = 'activerecordtest_neighbor';
	static protected $defaults = array(
		'name' => null,
	);
}

class ActiveRecordTest extends PHPUnit_Framework_TestCase {
	/**
	 * @var PDO
	 */
	private $pdo;
	
	function __construct()
	{
		parent::__construct();
		$this->pdo = new PDO("mysql:host=localhost;dbname=test", "root");
		ActiveRecord::setConnection($this->pdo);
	}
	
	function testTableMethods()
	{
		$id = $this->addRow();
		
		$updatedRow = array('name' => 'test updated', 'id' => $id);
		$this->assertTrue(Test::update($updatedRow) === $id);
		$this->assertTrue(Test::find($id)->fetchOne() == new Test($updatedRow));
		
		$s = Test::findAll();
		$sc = $s->count();
		$this->assertTrue($s->count() == 1, "Test::findAll(NO ARGUMENTS) failed (incorrect count $sc)");
		$sf = $s->query()->fetch(PDO::FETCH_ASSOC);
		$this->assertTrue($s->query()->fetch(PDO::FETCH_ASSOC) == $updatedRow, "Test::findAll(NO ARGUMENTS) failed (incorrect row " . var_export($sf, 1) . ")");
		
		$s = Test::findAll(array('name = ?' => $updatedRow['name']));
		$sc = $s->count();
		$this->assertTrue($s->count() == 1, "Test::findAll(EXISTING_ROW) failed (incorrect count $sc)");
		$sf = $s->query()->fetch(PDO::FETCH_ASSOC);
		$this->assertTrue($sf == $updatedRow, "Test::findAll(EXISTING_ROW) failed (incorrect row " . var_export($sf, 1) . ")");
		
		$s = Test::findAll(array('name = ?' => 'non-existing'));
		$sc = $s->count();
		$this->assertTrue($s->count() == 0, "Test::findAll(NON_EXISTING_ROW) failed (incorrect count $sc)");
		$sf = $s->query()->fetch(PDO::FETCH_ASSOC);
		$this->assertTrue($sf == false, "Test::findAll(NON_EXISTING_ROW) failed (incorrect row " . var_export($sf, 1) . ")");
	}
	
	function testRowMethods()
	{
		$id = $this->addRow();
		
		$tr = new Test();
		$loaded = $tr->load($id);
		$this->assertTrue('test' == $loaded['name']);
		
		$newname = 'test updated again';
		$tr->name = $newname;
		$save = $tr->save();
		$this->assertTrue($save > 0);
		$this->assertTrue(Test::count() == 1, "Test::count() failed");
		
		$tr->name = null;
		$this->assertTrue(! $tr->save(), "Test->save() should have failed (required name rule should fail)!");
		
		$tr->name = array('123');
		$this->assertTrue(! $tr->save(), "Test->save() should have failed (string name rule should fail)!");
		
		$tr2 = new Test();
		$tr2->name = '000 second row';// three zeroes - to ensure it sorts above the first one
		$this->assertTrue($tr2->save() > 0);
		
		$s = Test::findAll();
		$s->order('name');
		$s->paginate(1, 1);
		$all = $s->query()->fetchAll();
		$this->assertTrue(1 == count($all), 'Select->count() failed after pagination');
		$this->assertTrue($tr2->name == $all[0]->name, 'Select->query() failed ordering');
	}
	
	function testGetChildrenAndGetParent()
	{
		$id = $this->addRow();
		
		$tr = new Test();
		$tr->load($id);
		
		$child = new TestChild();
		$child->parent_id = $id;
		$child->name = 'child';
		$child->save();
		
		$sc = $tr->getChildObjects();
		$this->assertEquals(1, $sc->count());
		$child_row = $sc->fetchOne();
		$this->assertTrue($child_row instanceof TestChild);
		$this->assertEquals($child->name, $child_row->name);
		
		$sp = $child->getParentObject();
		$this->assertEquals(1, $sp->count());
		$parent_row = $sp->fetchOne();
		$this->assertTrue($parent_row instanceof Test);
		$this->assertEquals($tr->name, $parent_row->name);
	}
	
	function testGetNeighbors()
	{
		$id = $this->addRow();
		$tr = new Test();
		$tr->load($id);
		
		$neighbor = new TestNeighbor();
		$neighbor->name = 'neighbor';
		$neighbor->save();
		
		$this->pdo->query('INSERT INTO test_has_neighbors (test_id, neighbor_id) VALUES (' . $id . ', ' . $neighbor->id . ')');
		
		$sn = $tr->getNeighborObjects();
		$this->assertEquals(1, $sn->count());
		$neighbor_row = $sn->fetchOne();
		$this->assertTrue($neighbor_row instanceof TestNeighbor);
		$this->assertEquals($neighbor->name, $neighbor_row->name);
	}
	
	function testNeighborBindingOperations()
	{
		$id = $this->addRow();
		$tr = new Test();
		$tr->load($id);
		
		$neighbor = new TestNeighbor();
		$neighbor->name = 'neighbor';
		$neighbor->save();
		
		$tr->bindNeighbors(array($neighbor->id));
		
		$sn = $tr->getNeighborObjects();
		$this->assertEquals(1, $sn->count());
		$neighbor_row = $sn->fetchOne();
		$this->assertTrue($neighbor_row instanceof TestNeighbor);
		$this->assertEquals($neighbor->name, $neighbor_row->name);
		
		$tr->unbindNeighbor($neighbor->id);
		$sn = $tr->getNeighborObjects();
		$this->assertEquals(0, $sn->count());
	}
	
	function setUp()
	{
		$this->cleanup();
		$this->pdo->query("CREATE TABLE activerecordtest (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
			name VARCHAR(255))");
		$this->pdo->query("CREATE TABLE activerecordtest_child (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
			parent_id INT NOT NULL, 
			name VARCHAR(255))");
		$this->pdo->query("CREATE TABLE activerecordtest_neighbor (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
			name VARCHAR(255))");
		$this->pdo->query("CREATE TABLE test_has_neighbors (test_id INT NOT NULL, neighbor_id INT NOT NULL, 
			PRIMARY KEY (test_id, neighbor_id))");
	}
	function tearDown()
	{
		$this->cleanup();
	}
	
	function cleanup()
	{
		try {
			$this->pdo->query("DROP TABLE activerecordtest");
			$this->pdo->query("DROP TABLE activerecordtest_child");
			$this->pdo->query("DROP TABLE test_has_neighbors");
		} catch (PDOException $e) {}
	}
	
	private function addRow()
	{
		$ret = Test::insert(array('name' => 'test'));
		$this->assertTrue($ret > 0);
		return $ret;
	}
}